﻿using System;
using System.Windows.Media;
using System.Windows;

namespace Gencode.VECore.VectorShape
{
    public class VECCurve : VECShape
    {
        PathGeometry _definingGeometry;
        /// <summary>
        /// Serializer object description
        /// </summary>
        private const string serLineStart = "CurveLineStart";
        private const string serLineEnd = "CurveLineEnd";
        private const string serCurveStart = "CurveStart";
        private const string serCurveEnd = "CurveEnd";


        public VECCurve()
            : this(0, 0, 1, 0)
        {
        }

        public VECCurve(Point start, Point end)
            : this(start.X, start.Y, end.X, end.Y)
        {
        }

        public VECCurve(double x1, double y1, double x2, double y2)
			: base()
		{
            _definingGeometry = new PathGeometry();

            BezierSegment bezier = new BezierSegment(
                new Point(x1, y1)/*point 1 of curve*/,
                new Point(x2, y2)/*point 2 of curve*/,
                new Point(x2, y2)/*end of line*/,
                true);

            PathSegmentCollection segmentCollection = new PathSegmentCollection();
            segmentCollection.Add(bezier);


            PathFigure figure = new PathFigure(
                new System.Windows.Point(x1, y1)/*start of line*/, 
                segmentCollection, 
                false);

            _definingGeometry.Figures.Add(figure);
            

            this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MouseLeftUp);

            this.Focusable = true;
        }

        protected override Geometry DefiningGeometry
        {
            get
            {
                if (_definingGeometry == null)
                    _definingGeometry = new PathGeometry();
                return _definingGeometry;
            }
        }

        public Point StartPoint
        {
            get
            {
                return _definingGeometry.Figures[0].StartPoint;
            }
            set
            {
                _definingGeometry.Figures[0].StartPoint = value;
                this.InvalidateVisual();
            }
        }

        public Point EndPoint
        {
            get
            {
                return ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point3;
            }
            set
            {
                ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point3 = value;
                this.InvalidateVisual();
            }
        }

        public Point StartCurvePoint
        {
            get
            {
                return ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point1;
            }
            set
            {
                ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point1 = value;
                this.InvalidateVisual();
            }
        }

        public Point EndCurvePoint
        {
            get
            {
                return ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point2;
            }
            set
            {
                ((BezierSegment)_definingGeometry.Figures[0].Segments[0]).Point2 = value;
                this.InvalidateVisual();
            }
        }


        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            if (Selected)
            {
                Pen p = new Pen(Brushes.Blue, 1);

                drawingContext.DrawEllipse(Brushes.LightBlue, p, StartPoint, 2.5, 2.5);
                drawingContext.DrawEllipse(Brushes.LightBlue, p, StartCurvePoint, 2.5, 2.5);
                drawingContext.DrawEllipse(Brushes.LightBlue, p, EndCurvePoint, 2.5, 2.5);
                drawingContext.DrawEllipse(Brushes.LightBlue, p, EndPoint, 2.5, 2.5);

                drawingContext.DrawLine(p, StartPoint, StartCurvePoint);
                drawingContext.DrawLine(p, EndPoint, EndCurvePoint);
            }
        }

        private void MouseLeftUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            if (CanvasDocument.SelectedToolType == Gencode.VECore.ToolType.SelectionTool)
            {
                if (!Selected)
                {
                    Selected = true;
                }
            }
        }

        public override VECShape Clone()
        {
            var shape = new VECCurve();
            shape.StartPoint = this.StartPoint;
            shape.StartCurvePoint = this.StartCurvePoint;
            shape.EndCurvePoint = this.EndCurvePoint;
            shape.EndPoint = this.EndPoint;

            CopyShapeProperies(shape);
            return shape;
        }

        public override int HandleCount
        {
            get
            {
                return 4;
            }
        }

        /// <summary>
        /// Get handle point by 1-based number
        /// </summary>
        /// <param name="handleNumber"></param>
        /// <returns></returns>
        public override Point GetHandle(int handleNumber)
        {
            if (handleNumber == 1)
                return StartPoint;
            else if (handleNumber == 2)
                return StartCurvePoint;
            else if (handleNumber == 3)
                return EndCurvePoint;
            else
                return EndPoint;
        }

        /// <summary>
        /// Hit test.
        /// Return value: -1 - no hit
        ///                0 - hit anywhere
        ///                > 1 - handle number
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public override int HitTest(Point point)
        {
            if (Selected)
            {
                for (int i = 1; i <= HandleCount; i++)
                {
                    if (GetHandleRectangle(i).Contains(point))
                        return i;
                }
            }

            if (PointInObject(point))
                return 0;

            return -1;
        }

        protected override bool PointInObject(Point point)
        {
            return _definingGeometry.StrokeContains(new Pen(this.Stroke, this.StrokeThickness), point);
        }

        public override bool IntersectsWith(Rect rectangle)
        {
            var rectgeo = new RectangleGeometry(rectangle, 0, 0);
            var det = _definingGeometry.StrokeContainsWithDetail(new Pen(this.Stroke, this.StrokeThickness), rectgeo);

            if (det == IntersectionDetail.FullyContains || det == IntersectionDetail.FullyInside || det == IntersectionDetail.Intersects)
                return true;

            return false;
        }

        public override System.Windows.Input.Cursor GetHandleCursor(int handleNumber)
        {
            switch (handleNumber)
            {
                case 1:
                case 2:
                case 3:
                case 4:
                    return System.Windows.Input.Cursors.SizeAll;
                default:
                    return System.Windows.Input.Cursors.Arrow;
            }
        }

        public override void MoveHandle(Point point, int handleNumber)
        {
            if (handleNumber == 1)
                StartPoint = point;
            else if (handleNumber == 2)
                StartCurvePoint = point;
            else if (handleNumber == 3)
                EndCurvePoint = point;
            else
                EndPoint = point;

            this.InvalidateVisual();

            CallChangedHandler();
        }

        public override void Move(double deltaX, double deltaY)
        {
            StartPoint = new Point(StartPoint.X + deltaX, StartPoint.Y + deltaY);
            StartCurvePoint = new Point(StartCurvePoint.X + deltaX, StartCurvePoint.Y + deltaY);
            EndCurvePoint = new Point(EndCurvePoint.X + deltaX, EndCurvePoint.Y + deltaY);
            EndPoint = new Point(EndPoint.X + deltaX, EndPoint.Y + deltaY);

            CallMovedHandler();
        }

        public override void SaveToStream(System.Runtime.Serialization.SerializationInfo info, int orderNumber)
        {
            info.AddValue(
                String.Format(System.Globalization.CultureInfo.InvariantCulture,
                "{0}{1}",
                serLineStart, orderNumber),
                StartPoint);

            info.AddValue(
                String.Format(System.Globalization.CultureInfo.InvariantCulture,
                "{0}{1}",
                serLineEnd, orderNumber),
                EndPoint);

            info.AddValue(
                String.Format(System.Globalization.CultureInfo.InvariantCulture,
                "{0}{1}",
                serCurveStart, orderNumber),
                StartCurvePoint);

            info.AddValue(
                String.Format(System.Globalization.CultureInfo.InvariantCulture,
                "{0}{1}",
                serCurveEnd, orderNumber),
                EndCurvePoint);

            base.SaveToStream(info, orderNumber);
        }

        public override void LoadFromStream(System.Runtime.Serialization.SerializationInfo info, int orderNumber)
        {
            StartPoint = (Point)info.GetValue(
                String.Format(System.Globalization.CultureInfo.InvariantCulture,
                "{0}{1}",
                serLineStart, orderNumber),
                typeof(Point));

            EndPoint = (Point)info.GetValue(
                String.Format(System.Globalization.CultureInfo.InvariantCulture,
                "{0}{1}",
                serLineEnd, orderNumber),
                typeof(Point));

            StartCurvePoint = (Point)info.GetValue(
                String.Format(System.Globalization.CultureInfo.InvariantCulture,
                "{0}{1}",
                serCurveStart, orderNumber),
                typeof(Point));

            EndCurvePoint = (Point)info.GetValue(
                String.Format(System.Globalization.CultureInfo.InvariantCulture,
                "{0}{1}",
                serCurveEnd, orderNumber),
                typeof(Point));

            base.LoadFromStream(info, orderNumber);
        }
    }
}
